(ns packet.checksum
  (:require [packet.utils :as u]))

;;----------------------------------------------------------------------

(defn cs8
  "计算检查和"
  [bs]
  (->> bs (map unchecked-byte) (apply +) (bit-and 0xff)))

(defn cs16
  "计算16位校验和，固定以高字节在前的方式计算
  按两字节一组相加，高字节在前。字节数为奇数时在最后补一个0字节
  和超出16位时多出的加到低16位，最后按位取反
  将校验码放在一起校验时结果为0，这是验证的方法
  这是ip报头的校验方式"
  [bs]
  (loop [s (reduce (fn [s [b1 b2]]
                     (+ s
                        (bit-shift-left (unchecked-byte b1) 8)
                        (unchecked-byte (or b2 0))))
                   0
                   (partition-all 2 bs))]
    (let [n (unsigned-bit-shift-right s 16)]
      (if (= n 0)
        (bit-and (bit-not s) 0xffff)
        (recur (+ (bit-and s 0xffff) n))))))

(defn lrc
  "纵向冗余校验"
  [bs]
  (reduce bit-xor 0 bs))

(defn crc8
  "8位循环冗余校验，生成多项式为(x^8 + x^2 + x + 1)"
  [bs]
  (let [poly 0x07] ; 
    (reduce (fn [crc byte]
              (let [crc (bit-xor crc byte)]
                (reduce (fn [crc _]
                          (cond-> (bit-shift-left crc 1) (bit-test crc 7)
                                  (bit-xor poly)))
                        crc
                        (range 8))))
            0
            bs)))

(defn crc16-ccitt
  "16位循环冗余校验，生成多项式为(x^16 + x^12 + x^5 + 1)"
  [bs]
  (let [poly 0x1021]
    (reduce (fn [crc byte]
              (let [crc (bit-xor (bit-shift-left crc 8) (bit-and byte 0xFF))]
                (reduce (fn [crc _]
                          (cond-> (bit-shift-left crc 1) (bit-test crc 15)
                                  (bit-xor poly)))
                        crc
                        (range 8))))
            0xFFFF
            bs)))



(defn- reflect-bits
  "反转数据的位顺序"
  [x n]
  (loop [x x reflected 0 bits n]
    (if (zero? bits)
      reflected
      (recur (bit-shift-right x 1)
             (bit-or (bit-shift-left reflected 1) (bit-and x 1))
             (dec bits)))))

(defn crc16-modbus
  "循环冗余校验，生成多项式为(x^16 + x^15 + x^2 + 1)"
  [bs]
  (let [poly (reflect-bits 0x8005 16)]
    (reduce (fn [crc byte]
              (let [byte (reflect-bits (bit-and byte 0xFF) 8)
                    crc (bit-xor crc (bit-shift-left byte 8))]
                (reduce (fn [crc _]
                          (if (zero? (bit-and crc 0x8000))
                            (bit-shift-left crc 1)
                            (bit-xor (bit-shift-left crc 1) poly)))
                        crc
                        (range 8))))
            0xFFFF
            bs)))

